﻿using System;
using System.Text;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using LeanEngine.Entity;
using LeanEngine.Data.Linq.Language;
using LeanEngine.Data.Linq.Extension;

namespace LeanEngine.Data.Linq.Translator
{
    internal sealed class SelectTranslator : LinqTranslator<SelectToken>
    {
        #region Declaration Members

        private SelectToken _selectToken;
        private QueryContext _context;

        #endregion

        #region Constructor Members

        public SelectTranslator(TableToken table)
        {
            this._selectToken = new SelectToken()
            {
                ColumnList = table.Columns
            };
        }

        #endregion

        #region PropertyInfo Members

        internal ReadOnlyCollection<MemberBinding> CustomBindings { get; set; }

        internal LambdaExpression Projection { get; private set; }

        #endregion

        #region LinqTranslator<SelectToken> Members

        internal override bool CanEvaluate(QueryContext context)
        {
            switch(context.CurrentMethodCall.Method.Name)
            {
                case "Select":
                    Projection = GetLambdaWithParamCheck(context.CurrentMethodCall);
                    return Projection != null;

                case "Distinct":
                    _selectToken.Distinct = true;
                    return false;

                case "Count":
                    _selectToken.Count = true;
                    return false;

                case "GetPage":
                    var constant = context.CurrentMethodCall.Arguments[1] as ConstantExpression;
                    _selectToken.PageIndex = Convert.ToInt32(constant.Value);
                    constant = context.CurrentMethodCall.Arguments[2] as ConstantExpression;
                    _selectToken.PageSize = Convert.ToInt32(constant.Value);
                    return false;

                default:
                    return false;
            }
        }

        internal override SelectToken Interpret(QueryContext context)
        {
            _context = context;

            if (Projection != null)
            {
                var memberInitExp = Projection.Body as MemberInitExpression;
                if (memberInitExp != null) TranslateCustomBinding(memberInitExp.Bindings);
            }

            return _selectToken;
        }

        #endregion

        #region Overrided Method Members

        protected override Expression EvaluateMemberAccess(MemberExpression m)
        {
            var table = _context.QueryableTableList.Where(m.Expression.Type);
            this._selectToken.ColumnList.Add(table.Columns.Where(c => c.PropertyName == m.Member.Name).First());

            return m;
        }

        #endregion

        #region Method Members

        public void TranslateCustomBinding(ReadOnlyCollection<MemberBinding> bindings)
        {
            this.CustomBindings = bindings;
            this._selectToken.ColumnList = new List<ColumnToken>();

            foreach (var item in bindings)
            {
                EvaluateMemberAssignment(item as MemberAssignment);
            }
        }

        #endregion
    }
}
